"""
@function
Report animes episodes via email.

@cron
20 9 * * *

@command
python3 report.py

@detail
This script retrieves the previous day's episodes table from 
'~/.my-tools/data/anime/episodes_{date}.csv'and filters other
data based on the "interests" list in './config.json'.
Finally, organize a specific HTML page to be sent to specified recipients via email.

The fields of csv files:
episode, published_time, author, link, magnet_uri

The contents of the config.json:
{
  "interests": [
    {
      "name": "Re0: 从零开始的异世界生活 第三季 袭击篇",
      "alias": [
        "从零开始",
        "從零開始"
      ],
      "img_uri": "xxx",
      "receivers": [
        "alonely2021@qq.com"
      ]
    },
    {
      "name": "夏目友人帐 第七季",
      "alias": [
        "夏目友人帳"
      ],
      "img_uri": "xxx",
      "receivers": [
        "alonely2021@qq.com"
      ]
    }
  ],
  "smtp_settings": {
    "server": "smtp.qq.com",
    "port": 465,
    "user": "xxxx@qq.com",
    "password": "xxxxxx"
  }
}
"""
import io
import os
import csv
import json
import random
import pprint
import smtplib
import logging
from email.mime.text import MIMEText
from datetime import datetime, timedelta
from email.mime.multipart import MIMEMultipart
from logging.handlers import RotatingFileHandler
from jinja2 import Environment, FileSystemLoader

config_file_path = './config.json'
interests_file_path = './interests.json'
log_file = os.path.expanduser('~/.my-tools/log/anime/report.log')
data_dir = os.path.expanduser('~/.my-tools/data/anime/')

# Ensure the directories for logging and data exist
os.makedirs(os.path.dirname(log_file), exist_ok=True)
os.makedirs(data_dir, exist_ok=True)

# Set up logging
log_formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler = RotatingFileHandler(log_file, maxBytes=100*1024*1024, backupCount=2)  # 100 MB per file, 2 backup
handler.setFormatter(log_formatter)
logger = logging.getLogger('report')
logger.setLevel(logging.DEBUG)
logger.addHandler(handler)

def load_config(file_path):
    with open(file_path, 'r', encoding='utf-8') as f:
        config_data = json.load(f)
    return config_data

def log_pretty_print(data):
    output = io.StringIO()
    pprint.pprint(data, stream=output)
    contents = output.getvalue()
    output.close()
    return contents

def fetch_episodes_from_csv():
    today = datetime.now()
    yesterday = today - timedelta(days=1)
    yesterday_str = yesterday.strftime('%Y-%m-%d')
    filename = os.path.join(data_dir, f"episodes_{yesterday_str}.csv")
    episodes = []
    if not os.path.exists(filename):
        logger.error(f"File: {filename} is not exists, read nothing.")
        exit(1)

    with open(filename, mode='r', encoding='utf-8') as csvfile:
        reader = csv.DictReader(csvfile)
        for row in reader:
            episode = {
                'Episode': row['episode'],
                'Time': row['published_time'],
                'Author': row['author'],
                'Link': row['link'],
                'MagnetURI': row['magnet_uri']
            }
            episode_str = episode['Episode']
            logger.debug(f'Read entry from {filename}: "{episode_str}".')
            episodes.append(episode)

    logger.info(f"Read {len(episodes)} entries from {filename} .")
    return episodes

def filter_episodes_by_receiver(episodes, interests):
    # receiver_data is a dict("email reciver"-"interests dict")
    # interests dict:("name"-"episodes list")
    receiver_data = {}
    for episode in episodes:
        for interest in interests:
            if any(alias in episode['Episode'] for alias in [interest['name']] + interest.get('alias', [])):
                for receiver in interest['receivers']:
                    if receiver not in receiver_data:
                        receiver_data[receiver] = {}
                    if interest['name'] not in receiver_data[receiver]:
                        receiver_data[receiver][interest['name']] = []
                    episode_with_img = episode.copy()
                    episode_with_img['img_uri'] = interest['img_uri']  # Add image URI to episode data
                    receiver_data[receiver][interest['name']].append(episode_with_img)
    return receiver_data

def create_email_content(receiver_data, all_episodes, yesterday_str, episodes_keys):
    file_loader = FileSystemLoader('templates')
    env = Environment(loader=file_loader)
    template = env.get_template('template.html')

    content = template.render(receiver_data=receiver_data, all_episodes=all_episodes, yesterday_str=yesterday_str, episodes_keys=episodes_keys)
    return content

def send_email(subject, content, receivers, smtp_server, smtp_port, smtp_user, smtp_password):
    try:
        for receiver in receivers:
            server = smtplib.SMTP_SSL(smtp_server, smtp_port)
            server.login(smtp_user, smtp_password)
            msg = MIMEMultipart()
            msg['From'] = smtp_user
            msg['Subject'] = subject
            msg['To'] = receiver
            msg.attach(MIMEText(content, 'html'))
            server.send_message(msg)
        logger.info(f"Send email success, receiver(s): {', '.join(receivers)}")
    except Exception as e:
        logger.error(f"Send email failed: {e}")

def main():
    logger.info("--------------------------Starting script: daily_anime_report.py--------------------------")
    config = load_config(config_file_path)
    interests_config = load_config(interests_file_path)

    interests = interests_config.get('interests', None)
    if interests is None:
        logger.error("There are no interests in the config.json.")
        return

    episodes = fetch_episodes_from_csv()
    receiver_data = filter_episodes_by_receiver(episodes, interests)
    logger.debug("Filtered episodes data:\n" + log_pretty_print(receiver_data))

    today = datetime.now()
    yesterday = today - timedelta(days=1)
    yesterday_str = yesterday.strftime('%Y-%m-%d')
    
    smtp_settings = config.get('smtp_settings', {})
    smtp_server = smtp_settings.get('server')
    smtp_port = smtp_settings.get('port')
    smtp_user = smtp_settings.get('user')
    smtp_password = smtp_settings.get('password')
    for receiver, episodes_data in receiver_data.items():
        email_content = create_email_content(episodes_data, episodes, yesterday_str, list(episodes_data.keys()))
        random_key = random.choice(list(episodes_data.keys()))
        subject = f'您关注的《{random_key}》等动漫有新的资源了喵~'
        logger.debug(f'{receiver}: {subject}')
        send_email(subject, email_content, [receiver], smtp_server, smtp_port, smtp_user, smtp_password)
    
    logger.info("--------------------------Finished script: daily_anime_report.py--------------------------")

if __name__ == "__main__":
    main()